package com.ygqh.common.xml;

import com.ygqh.common.constants.Consts;
import com.ygqh.common.utils.ReflectionUtil;
import com.ygqh.common.utils.StringUtil;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.stream.*;
import javax.xml.transform.stream.StreamSource;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 对 后缀为_$n 的 xml节点反序列化
 *
 * @author jinyu(foxinmy @ gmail.com)
 * @className ListsuffixResultDeserializer
 * @date 2015年3月24日
 * @see
 * @since JDK 1.6
 */
public class ListsuffixResultDeserializer {

    private static Pattern DEFAULT_PATTERN;

    static {
        String regex = null;
        try {
            Object value = ListsuffixResult.class.getMethod("value").getDefaultValue();
            if (value instanceof String) {
                regex = (String) value;
            } else if (value instanceof String[]) {
                regex = ((String[]) value)[0];
            }
        } catch (NoSuchMethodException e) {
            ;
        }
        if (StringUtil.isBlank(regex)) {
            regex = "(_\\d)$";
        }
        DEFAULT_PATTERN = Pattern.compile(regex);
    }

    /**
     * 对包含$n节点的xml反序列化
     *
     * @param content xml内容
     * @param clazz
     * @return
     */
    public static <T> T deserialize(String content, Class<T> clazz) {
        T t = XmlStream.fromXML(content, clazz);
        Map<Field, String[]> listsuffixFields = getListsuffixFields(clazz);
        if (!listsuffixFields.isEmpty()) {
            for (Entry<Field, String[]> entry : listsuffixFields.entrySet()) {
                Field field = entry.getKey();
                Type type = field.getGenericType();
                Class<?> wrapperClazz = null;
                if (type instanceof ParameterizedType) {
                    wrapperClazz = (Class<?>) ((ParameterizedType) type).getActualTypeArguments()[0];
                } else {
                    continue;
                }
                ListWrapper<?> listWrapper = deserializeToListWrapper(content, wrapperClazz, entry.getValue());
                if (listWrapper != null) {
                    try {
                        field.setAccessible(true);
                        field.set(t, listWrapper.getItems());
                    } catch (Exception e) {
                        ;
                    }
                }
            }
        }
        return t;
    }

    @SuppressWarnings("unchecked")
    public static <T> ListWrapper<T> deserializeToListWrapper(String content, Class<T> clazz, String... matchPattern) {
        XMLStreamReader xr = null;
        XMLStreamWriter xw = null;
        try {
            xr = XMLInputFactory.newInstance().createXMLStreamReader(new StringReader(content));
            List<Pattern> patterns = new ArrayList<Pattern>();
            for (String pattern : matchPattern) {
                patterns.add(Pattern.compile(pattern));
            }
            Matcher matcher = null;
            Map<String, Map<String, String>> outMap = new HashMap<String, Map<String, String>>();
            while (true) {
                int event = xr.next();
                if (event == XMLStreamConstants.END_DOCUMENT) {
                    break;
                } else if (event == XMLStreamConstants.START_ELEMENT) {
                    String name = xr.getLocalName();
                    for (Pattern pattern : patterns) {
                        if ((matcher = pattern.matcher(name)).find()) {
                            while (true) {
                                event = xr.next();
                                if (event == XMLStreamConstants.START_ELEMENT) {
                                    name = xr.getLocalName();
                                } else if (event == XMLStreamConstants.END_ELEMENT) {
                                    break;
                                } else if (event == XMLStreamConstants.CHARACTERS || event == XMLStreamConstants.CDATA) {
                                    String key = matcher.group();
                                    if (!pattern.pattern().equals(DEFAULT_PATTERN.pattern())) {
                                        matcher = DEFAULT_PATTERN.matcher(name);
                                        matcher.find();
                                        key = matcher.group();
                                    }
                                    Map<String, String> innerMap = null;
                                    if ((innerMap = outMap.get(key)) == null) {
                                        innerMap = new HashMap<String, String>();
                                        outMap.put(key, innerMap);
                                    }
                                    innerMap.put(name.replace(key, ""), xr.getText());
                                }
                            }
                            break;
                        }
                    }
                }
            }
            if (!outMap.isEmpty()) {
                StringWriter sw = new StringWriter();
                xw = XMLOutputFactory.newInstance().createXMLStreamWriter(sw);
                xw.writeStartDocument(Consts.UTF_8.name(), "1.0");
                xw.writeStartElement(clazz.getCanonicalName());
                String itemName = StringUtil.uncapitalize(clazz.getSimpleName());
                XmlRootElement rootElement = clazz.getAnnotation(XmlRootElement.class);
                if (rootElement != null && StringUtil.isNotBlank(rootElement.name())) {
                    try {
                        if (!rootElement.name().equals(XmlRootElement.class.getMethod("name").getDefaultValue().toString())) {
                            itemName = rootElement.name();
                        }
                    } catch (NoSuchMethodException e) {
                        ;
                    }
                }
                for (Entry<String, Map<String, String>> outE : outMap.entrySet()) {
                    xw.writeStartElement(itemName);
                    for (Entry<String, String> innerE : outE.getValue().entrySet()) {
                        xw.writeStartElement(innerE.getKey());
                        xw.writeCharacters(innerE.getValue());
                        xw.writeEndElement();
                    }
                    xw.writeEndElement();
                }
                xw.writeEndElement();
                xw.writeEndDocument();
                JAXBContext ctx = JAXBContext.newInstance(ListWrapper.class, clazz);
                Unmarshaller u = ctx.createUnmarshaller();
                return u.unmarshal(new StreamSource(new StringReader(sw.getBuffer().toString())), ListWrapper.class).getValue();
            }
            return null;
        } catch (XMLStreamException e) {
            throw new IllegalArgumentException(e);
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (xw != null) {
                    xw.close();
                }
                if (xr != null) {
                    xr.close();
                }
            } catch (XMLStreamException e) {
            }
        }
    }

    public static Map<Field, String[]> getListsuffixFields(Class<?> clazz) {
        Map<Field, String[]> listsuffixFields = new HashMap<Field, String[]>();
        Set<Field> allFields = ReflectionUtil.getAllField(clazz);
        ListsuffixResult listsuffixResult = null;
        for (Field field : allFields) {
            listsuffixResult = field.getAnnotation(ListsuffixResult.class);
            if (listsuffixResult != null) {
                listsuffixFields.put(field, listsuffixResult.value());
            }
        }
        return listsuffixFields;
    }
}
